﻿namespace Whut.ObjectSharePointMapper.Internals
{
    using System;
    using System.Globalization;
    using System.Text;
    using Microsoft.SharePoint.Utilities;
    using System.Collections.Generic;

    public static class CamlGenerator
    {
        public static string GetCamlQueryViewFields(ListInfo listInfo)
        {
            StringBuilder viewFields = new StringBuilder("<FieldRef ID='" + listInfo.Id.FieldId + "'/>");
            foreach (FieldInfo columnInfo in listInfo.Fields)
            {
                viewFields.Append(GetCamlQueryFieldRefElement(columnInfo, false));
            }

            return viewFields.ToString();
        }

        public static string GetCamlQueryWhere(FieldInfo fieldInfo, ConditionType conditionType, object fieldValue)
        {
            return "<Where>" + GetCamlQueryWhereCondition(conditionType, fieldInfo, fieldValue) + "</Where>";
        }

        public static string GetCamlQueryWhere(List<ConditionInfo> conditionInfos)
        {
            String where = string.Empty;
            if (conditionInfos.Count > 0)
            {
                where = GetCamlQueryWhereCondition(conditionInfos[0]);
                for(int i = 1; i < conditionInfos.Count; i++)
                {
                    ConditionInfo conditionInfo = conditionInfos[i];
                    switch (conditionInfo.Operator)
                    {
                        case ConditionOperator.And:
                            where = "<And>" + where + GetCamlQueryWhereCondition(conditionInfo) + "</And>";
                            break;
                        case ConditionOperator.Or:
                            where = "<Or>" + where + GetCamlQueryWhereCondition(conditionInfo) + "</Or>";
                            break;
                        default:
                            break;
                    }
                }
            }
            return "<Where>" + where + "</Where>";
        }

        private static string GetCamlQueryWhereCondition(ConditionInfo conditionInfo)
        {
            return GetCamlQueryWhereCondition(conditionInfo.Type, conditionInfo.FieldInfo, conditionInfo.FieldValue);
        }

        private static string GetCamlQueryWhereCondition(ConditionType conditionType, FieldInfo fieldInfo, object fieldValue)
        {
            string start, end;
            switch (conditionType)
            {
                case ConditionType.Equal:
                    start = "<Eq>";
                    end = "</Eq>";
                    break;
                case ConditionType.LessOrEqual:
                    start = "<Leq>";
                    end = "</Leq>";
                    break;
                case ConditionType.GreaterOrEqual:
                    start = "<Geq>";
                    end = "</Geq>";
                    break;
                case ConditionType.Less:
                    start = "<Lt>";
                    end = "</Lt>";
                    break;
                case ConditionType.Greater:
                    start = "<Gt>";
                    end = "</Gt>";
                    break;
                default:
                    throw new ArgumentOutOfRangeException("type", "Not supported ConditionType.");
            }

            return start + GetCamlQueryFieldRefElement(fieldInfo, true) + GetCamlQueryValueElement(fieldInfo, fieldValue) + end;
        }

        private static string GetCamlQueryFieldRefElement(FieldInfo fieldInfo, bool lookupIdQuery)
        {
            string begin = "<FieldRef ID='" + fieldInfo.FieldId;
            string end = "'/>";
            if (lookupIdQuery && fieldInfo.PropertyType == typeof(Lookup))
            {
                return begin + "' LookupId='TRUE" + end;
            }

            return begin + end;
        }

        private static string GetCamlQueryValueElement(FieldInfo fieldInfo, object propertyValue)
        {
            // TODO a można też te nazwy wygenerować z SPFieldType, dla Fielda od itema:)
            string type, value;
            if (fieldInfo.PropertyType == typeof(double))
            {
                type = "Number";
                value = ((double)propertyValue).ToString(CultureInfo.InvariantCulture);
            }
            else if (fieldInfo.PropertyType == typeof(string))
            {
                type = "Text";
                value = (string)propertyValue;
            }
            else if (fieldInfo.PropertyType == typeof(Lookup))
            {
                type = "Lookup";
                value = ((Lookup)propertyValue).Id.ToString(CultureInfo.InvariantCulture);
            }
            else if (fieldInfo.PropertyType == typeof(DateTime))
            {
                type = "DateTime";
                value = SPUtility.CreateISO8601DateTimeFromSystemDateTime((DateTime)propertyValue);
            }
            else if (fieldInfo.PropertyType.IsEnum)
            {
                // Not Choice, because now it works with ContentType field also
                // może zrobić sobie własną klase enum dziedziczaca p normalnym, by rozróznić ct od zwyklego choice
                // albo lepiej, zrob taki atrybut, ze ten enum to od choice, a ten enum to od content typu
                // TODO
                // by było pole SPListItem w encji
                // By session cache współdziałał dobrze, z encjami które dziedziczą po innych
                // CO bym mógł najpierw sciągnąc encje, a potem jej pochodna, ale nie, bo ViewFields
                type = "Text";
                value = TypeConverter.EnumToString(propertyValue);
            }
            else
            {
                throw new ArgumentOutOfRangeException("fieldInfo", "Not supported entity property type.");
            }

            return "<Value Type='" + type + "'>" + value + "</Value>";
        }
    }
}
